//    Copyright (C) Kherty.  All rights reserved.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using OpenLS.Core.Serialization;

namespace OpenLS.Spreadsheet
{
	public class AutoFilter: IOfficeXmlSerializable
	{
		public  AutoFilter(Range range)
		{
			Contract.Requires(range != null);
			_range = range;
			foreach (Column c in range.GetColumns())
			{
				FilterColumns.Add(new FilterColumn(this, _range, c));
			}
		}
		public AutoFilter()
		{

		}
		#region IOfficeXmlSerializable Members

		void IOfficeXmlSerializable.ReadXml(ReadContext context)
		{
			using (ReadContext c = context.Read("autoFilter"))
			{
				Worksheet worksheet = c.GetData<Worksheet>();
				_range = worksheet.GetRange(c.GetOptionalString("ref", worksheet[0, 0].Label)); //\\sure?
				FilterColumns.Clear();
				inRead = true;
				while (c.ReaderLocalName == "filterColumn")
				{
					FilterColumn fc = new FilterColumn(this, Range);
					IOfficeXmlSerializable s = fc;
					s.ReadXml(c);
					FilterColumns.Add(fc);
					//\\FilterColumns.Add(c.ReadElement<FilterColumn>());
				}
				AddMissingColumns();
				SortState = c.ReadOptionalElement<SortState>("sortState");
				Extension = c.ReadOptionalElement<ExtensionElement>("extLst");
				inRead = false;
			}
		}

		private void AddMissingColumns()
		{
			int first = Range.TopLeftCell.Column.Index;
			int last = first + Range.ColumnSpan - 1;
			int i = 0;
			while (first <= last)
			{
				if (i < FilterColumns.Count)
				{
					FilterColumn c = FilterColumns[i];
					if (c.Column.Index == first)
					{
						first++;
						i++;
					}
					else
					{
						c = new FilterColumn(this, Range,Range.Worksheet.Columns[first]);
						FilterColumns.Add(c);
						first++;
						i++;
					}
				}
				else
				{
					FilterColumn c = new FilterColumn(this, Range, Range.Worksheet.Columns[first]);
					FilterColumns.Add(c);
					first++;
				}
			}
		}

		void IOfficeXmlSerializable.WriteXml(WriteContext context)
		{
			using (WriteContext c = context.Write("autoFilter"))
			{
				c.SetOptionalString("ref", Range.Label);
				if (FilterColumns != null)
					foreach (FilterColumn fc in FilterColumns)
						c.WriteElement(fc);
				c.WriteOptionalElement(sortState);
				c.WriteOptionalElement(Extension);
			}
		}

		private ExtensionElement _extension;

		public ExtensionElement Extension
		{
			get { return _extension; }
			set { _extension = value; }
		}
		private Range _range;

		public Range Range
		{
			get { return _range; }
			//\\set { _range = value; }
		}
	
		/*private string reference;

		public string Ref
		{
			get { return reference; }
			set { reference = value; }
		}*/

		private readonly ObservableCollection<FilterColumn> _filterColumns = new ObservableCollection<FilterColumn>();
		public ObservableCollection<FilterColumn> FilterColumns
		{
			get { return _filterColumns; }
		}
		private SortState sortState;
		private bool _inRead;
		private bool inRead
		{
			get { return _inRead; }
			set
			{
				if (_inRead == value)
					return;
				_inRead = value;
				if (!inRead)
					Apply();
			}
		}

			public SortState SortState
		{
			get { return sortState; }
			set { sortState = value; }
		}

		internal void Apply()
		{
			if (inRead)
				return;
			bool first = true;
			Dictionary<FilterColumn, RangeAddress> addresses = Enumerable.ToDictionary(FilterColumns, c => c,
																	   c =>
																	   new RangeAddress(
																		   new CellAddress(Range.TopLeftCell.Row.Index,
																						   c.Column.Index),
																		   new CellAddress(Range.TopLeftCell.Row.Index + Range.RowSpan,
																		   c.Column.Index)));
			Dictionary<FilterColumn, Func<Cell, bool>> evaluators = Enumerable.ToDictionary(FilterColumns, c => c,
																							c =>
																						   c.GetCellEvaluator(
																								c.RangeWithoutHeader));
			foreach (Row row in Range.GetRows())
			{
				if (first)
				{
					first = false;
					continue;
				}
				int index = Range.TopLeftCell.Column.Index;
				bool visible = true;
				foreach (FilterColumn column in FilterColumns)
				{
					if (column.Filter == null)
					{
						index++;
						continue;
					}
					Cell toTest = Range.Worksheet[row.Index, index];
					if (!evaluators[column](toTest))
					{
						visible = false;
						break;
					}
					index++;
				}
				row.IsVisible = visible;
			}
		}
	
	
		#endregion
	}
}
